package network

import (
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"fmt"
)

// Head 消息头
type Head struct {
	// 消息长度，包括自身在内
	Len int32
	// 消息协议ID，用于表示消息的类型
	ID int32
	// 请求编号，客户端生成的请求流水号，在请求的应答里原样返回，用于识别应答与请求的对应关系，客户端生成的流水号不可以为0
	// 如果是推送通知类消息，此字段固定为0。
	ReqID int32
	// 时间戳
	Timestap int32
	// 协议版本号
	Version int32
}

const (
	// HeadLen 为了免于计算 实时与Head结构对应
	HeadLen = 20
)

// Message 消息通用格式
// 客户端收到服务器的ack格式：
/*
	{"code":1,"content":{}}
*/
type Message struct {
	MsgHead Head
	Content []byte
}

// NewMessage : Message构造器
func NewMessage(msgID, reqID, timestap int32) *Message {
	return &Message{
		MsgHead: Head{
			ID:       msgID,
			ReqID:    reqID,
			Timestap: timestap,
			Version:  0,
		},
	}
}

// Unmarshal 反序列化message
// 如果网路接收数据不足消息头的长度，则返回错误
// 如果网络接收数据长度不足一个消息长度，则返回错误，同时返回截断的消息的长度
// 如果成功解析一条消息，则返回消息结构
func Unmarshal(buff []byte) (*Message, int, error) {
	// 消息头中消息长度字段是int32
	if len(buff) < HeadLen {
		return nil, 0, fmt.Errorf(`network.Unmarshal: data length less than head length`)
	}

	var msg Message
	buf := bytes.NewBuffer(buff)
	err := binary.Read(buf, binary.LittleEndian, &msg.MsgHead)
	if err != nil {
		return nil, 0, err
	}
	//log.Printf("消息长度[%d], 缓冲区长度：%d", int(msg.MsgHead.Len), maxlen)

	// 发生消息截断
	if int(msg.MsgHead.Len) > len(buff) {
		return nil, int(msg.MsgHead.Len), fmt.Errorf(`network.Unmarshal: expected message length %v, data length %v not enough`, msg.MsgHead.Len, len(buff))
	}

	if int(msg.MsgHead.Len) < HeadLen {
		return nil, 0, fmt.Errorf("network.Unmarshal: expected message length %v, HeadLen %v", msg.MsgHead.Len, HeadLen)
	}
	msg.Content = buff[HeadLen:msg.MsgHead.Len]
	//log.Printf("解析了一条消息：%d, 长度：%d, 使用的缓冲区长度：%d", msg.MsgHead.ID, msg.MsgHead.Len, maxlen)
	return &msg, 0, nil
}

// Marshal 序列化message
func Marshal(msg *Message, gzipStatus int32) []byte {
	buf := new(bytes.Buffer)

	if len(msg.Content) >= 512 && gzipStatus == 1 {
		msg.MsgHead.Version = 1
		var b bytes.Buffer
		w := gzip.NewWriter(&b)
		w.Write(msg.Content)
		w.Flush()
		w.Close()
		msg.Content = b.Bytes()
	}
	msg.MsgHead.Len = int32(len(msg.Content)) + int32(HeadLen)
	binary.Write(buf, binary.LittleEndian, &msg.MsgHead)
	buf.Write(msg.Content)
	return buf.Bytes()
}
